﻿using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Proggy.Data.Abstract;
using Proggy.Web.Cms.Context;
using Proggy.Web.Cms.ModelBinding;
using System.Configuration;
using Proggy.Web.Cms.ContentModel;
using Proggy.Data.Concrete;
using Proggy.Web.Cms.Repositories.Membership;
using System.Web.Security;
using Proggy.Web.Cms.Cache;
using Ninject;
using Proggy.Web.Cms.Repositories.ContentModel;
using Proggy.Web.Cms.Service;
using Proggy.Web.Cms.Configuration;
using System.Linq;
using Proggy.Web.Cms.Seo.Service.Abstract;
using Proggy.Web.Cms.Seo.Service.Concrete;
using Proggy.Web.Cms.Repositories.Published;

namespace Proggy.Web.Cms.AppHelpers
{
    /// <summary>
    /// The application class (which global.asax inherits from)
    /// </summary>
    public class ProggyHttpApplication : HttpApplication
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public ProggyHttpApplication()
        {
            // get service
            var service = DependencyResolver.Current.GetService<IUrlOptimizationService>();
            this.UrlOptimizationService = service;
        }

        /// <summary>
        /// Startup manager
        /// </summary>
        public static StartupManager AppStartupManager
        {
            get
            {
                if (_appStartupManager == null)
                {
                    _appStartupManager = new StartupManager();
                }
                return _appStartupManager;
            }
        }
        private static StartupManager _appStartupManager;

        /// <summary>
        /// Register global filters
        /// </summary>
        /// <param name="filters"></param>
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

        /// <summary>
        /// Register routes
        /// </summary>
        /// <param name="routes"></param>
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("{resource}.css/{*pathInfo}");
            routes.IgnoreRoute("{resource}.ico/{*pathInfo}");
            routes.IgnoreRoute("{resource}.png/{*pathInfo}");
            routes.IgnoreRoute("{resource}.jpg/{*pathInfo}");
            routes.IgnoreRoute("{resource}.jpeg/{*pathInfo}");
            routes.IgnoreRoute("{resource}.pdf/{*pathInfo}");
            routes.IgnoreRoute("{resource}.js/{*pathInfo}");
            routes.IgnoreRoute("{resource}.gif/{*pathInfo}");


            //
            var rootUrl = FrontEndRequestContext.GetRootUrl();

            // set url cache
            AppStartupManager.SetUrlCache(rootUrl);

            // get routes
            var theRoutes = AppStartupManager.GetOrderedNodeRoutes();

            // loop through urls and create routes
            foreach (var route in theRoutes)
            {

                // map
                routes.MapRoute(route.Name, route.UrlWithParameters, route.ParameterDefaults, route.NameSpaces);
            }


        }

        /// <summary>
        /// App start method
        /// </summary>
        protected void Application_Start()
        {
            this.ProggyApplicationStart();
        }

        /// <summary>
        /// App start
        /// </summary>
        public virtual void ProggyApplicationStart()
        {
            //
            AppStartupManager.SetWurfl();

            //
            AppStartupManager.RefreshData();

            // add sections
            AppStartupManager.SetSections();

            // add model binders
            ModelBinders.Binders.Add(typeof(IContentModelBase), new ContentModelBinder());

            // register areas
            AreaRegistration.RegisterAllAreas();

            // register filters
            RegisterGlobalFilters(GlobalFilters.Filters);

            // register routes
            RegisterRoutes(RouteTable.Routes);

            // set vm cache
            AppStartupManager.SetPublishedContentCache();

            // clear data
            AppStartupManager.DisposeDataContext();

        }

        /// <summary>
        /// End request
        /// </summary>
        protected void Application_EndRequest()
        {
            this.ProggyApplicationEndRequest();
        }

        /// <summary>
        /// End request
        /// </summary>
        public virtual void ProggyApplicationEndRequest()
        {
            // check 404 (standard)
            if (Context.Response.StatusCode == 404)
            {

                // handle
                this.HandleError(404);

            }
        }

        /// <summary>
        /// Begin request
        /// </summary>
        protected void Application_BeginRequest()
        {
            // continue
            this.ProggyApplicationBeginRequest();

        }

        /// <summary>
        /// Begin request
        /// </summary>
        public virtual void ProggyApplicationBeginRequest()
        {
            // get result
            var result = this.UrlOptimizationService.IsOptimizedOkAlready(this.Request.Url);

            // check
            if (!result.Item1 && this.Request.HttpMethod == "GET")
            {
                this.Response.RedirectPermanent(result.Item2.OriginalString, true);
                return;
            }
        }

        /// <summary>
        /// Acquire request state
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Application_AcquireRequestState()
        {
            this.ProggyApplicationAcquireRequestState();
        }

        /// <summary>
        /// Begin request
        /// </summary>
        public virtual void ProggyApplicationAcquireRequestState()
        {
            // set membership context
            SetMembershipContext();
        }

        /// <summary>
        /// Sets membership context
        /// </summary>
        public static void SetMembershipContext()
        {
            if (HttpContext.Current != null && HttpContext.Current.Session != null && HttpContext.Current.Request != null)
            {
                // 
                var needToSet =
                    HttpContext.Current.Request.IsAuthenticated && (
                    HttpContext.Current.Session[MembershipContext.MEMBERSHIP_SET_KEY] == null ||
                    !Convert.ToBoolean(HttpContext.Current.Session[MembershipContext.MEMBERSHIP_SET_KEY]));

                // check not already set
                if (needToSet)
                {
                    // create context
                    var ctx = new MembershipContext
                    {

                    };

                    // add
                    HttpContext.Current.Session.Add(MembershipContext.MEMBERSHIP_CONTEXT_KEY, ctx);

                    try
                    {
                        // get repo
                        var repo = DependencyResolver.Current.GetService<IMembershipRepository>();
                        // get current user
                        var currentMember = repo.GetMember(HttpContext.Current.User, true);
                        // set
                        HttpContext.Current.Session.Add(MembershipContext.MEMBERSHIP_KEY, currentMember);
                    }
                    catch
                    {

                    }

                    HttpContext.Current.Session.Add(MembershipContext.MEMBERSHIP_SET_KEY, true);
                }
            }
        }

        /// <summary>
        /// Remove membership context
        /// </summary>
        public static void RemoveMembershipContext()
        {
            if (HttpContext.Current != null)
            {
                HttpContext.Current.Session.Remove(MembershipContext.MEMBERSHIP_CONTEXT_KEY);
                HttpContext.Current.Session[MembershipContext.MEMBERSHIP_SET_KEY] = false;
                HttpContext.Current.Session.Remove(MembershipContext.MEMBERSHIP_KEY);
                FormsAuthentication.SignOut();
                HttpContext.Current.Session.Abandon();
            }
        }

        /// <summary>
        /// On error
        /// </summary>
        protected void Application_Error()
        {

            // get last error
            var error = Server.GetLastError();

            // get code
            var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

            // 
            this.HandleError(code);


        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="code"></param>
        public void HandleError(int code)
        {
            // check ajax
            if (!AjaxRequestExtensions.IsAjaxRequest(new HttpRequestWrapper(Request)))
            {

                // get page alias
                var pageAliasStr = ConfigurationManager.AppSettings["Proggy" + code + "ContentUniqueAlias"];

                // check
                if (!string.IsNullOrEmpty(pageAliasStr))
                {
                    try
                    {

                        // get content repo
                        var repo = CachedContentManager.GetRepositoryInstance(true);

                        // get content model
                        var model =
                            repo
                            .PublishedContent
                            .SingleOrDefault(x =>
                                x.UniqueAlias != null &&
                                x.UniqueAlias.ToLower() == pageAliasStr.ToLower()
                            );

                        // check
                        if (model != null)
                        {

                            // get view
                            // var view = "~/Views/ProggyTemplates/" + model.Template + ".cshtml";

                            // get controller
                            //var controller = (FrontEndControllerBase)model.GetControllerForContent();

                            // clear response
                            Response.Clear();

                            // get request context
                            var rc = Request.RequestContext;// ((MvcHandler)Context.CurrentHandler).RequestContext;

                            // set route data
                            rc.RouteData.Values["controller"] = model.TypeAlias;
                            rc.RouteData.Values["action"] = "HandleUrl";
                            rc.RouteData.Values["nodeId"] = model.Id;
                            rc.RouteData.Values["json"] = false;
                            rc.RouteData.Values["area"] = "";

                            // reset request context
                            FrontEndRequestContext.RefreshCurrent();

                            // get factory
                            var factory = ControllerBuilder.Current.GetControllerFactory();

                            // get controller
                            var controller = (IController)model.GetControllerForContent();

                            // execute
                            controller.Execute(rc);

                            // clear error
                            Server.ClearError();
                        }
                    }
                    catch
                    {
                        // DO NOTHING. THIS IS AN ERROR HANDLER!!!
                    }

                }
            }
        }

        /// <summary>
        /// Sets kernel
        /// </summary>
        /// <param name="kernel"></param>
        public static void SetNinjectKernel(IKernel kernel)
        {
            // bind REAL db context
            kernel
                .Bind<IProggyDataContext>()
                .To<ProggyDataContext>();


            // bind REAL node repo
            kernel
                .Bind<INodeRepository>()
                .To<NodeRepository>();

            // bind vm repo (REAL)
            kernel
                .Bind<IContentModelTypeRepository>()
                .To<ContentModelTypeRepository>()
                .InSingletonScope();

            // bind content data repo (REAL)
            kernel
                .Bind<IContentModelDataRepository>()
                .To<ContentModelDataRepository>();

            // bind content repo (REAL)
            kernel
                .Bind<IContentCrudService>()
                .To<ContentCrudService>();

            // bind memb data repo
            kernel
                .Bind<IMembershipDataRepository>()
                .To<MembershipDataRepository>();

            // bind memb repo (REAL)
            kernel
                .Bind<IMembershipRepository>()
                .To<MembershipRepository>();

            // bind random data (REAL)
            kernel
                .Bind<IRandomDataRepository>()
                .To<RandomDataRepository>();

            // bind to content converter
            kernel
                .Bind<IContentModelConverter>()
                .To<ContentModelConverter>();
            //.InSingletonScope();


            // bind email service
            kernel
                .Bind<IEmailService>()
                .To<EmailService>();
            //.InSingletonScope();

            // bind config
            kernel
                .Bind<IEmailConfiguration>()
                .To<EmailConfiguration>()
                .InSingletonScope()
                .WithPropertyValue("Host", ConfigurationManager.AppSettings["EmailHost"])
                .WithPropertyValue("Port", Convert.ToInt32(ConfigurationManager.AppSettings["EmailPort"]))
                .WithPropertyValue("Username", ConfigurationManager.AppSettings["EmailUsername"])
                .WithPropertyValue("Password", ConfigurationManager.AppSettings["EmailPassword"])
                .WithPropertyValue("FromAddress", ConfigurationManager.AppSettings["EmailFromAddress"])
                .WithPropertyValue("SiteName", ConfigurationManager.AppSettings["SiteName"]);

            #region url optimizer

            bool addWWW = false;
            bool useTrailingSlash = true;
            try
            {
                addWWW = Convert.ToBoolean(ConfigurationManager.AppSettings["UseWWW"]);
                useTrailingSlash = Convert.ToBoolean(ConfigurationManager.AppSettings["UseTrailingSlash"]);
            }
            catch
            {
                //
            }

            // url opt
            kernel
                .Bind<IUrlOptimizationService>()
                .To<UrlOptimizationService>()
                .InSingletonScope()
                .WithConstructorArgument("addWWW", addWWW)
                .WithConstructorArgument("useTrailingSlash", useTrailingSlash);

            #endregion

            // bind published repo
            kernel
                .Bind<IPublishedContentRepository>()
                .ToMethod(c =>
                {
                    try
                    {
                        return CachedContentManager.GetRepositoryInstance(true);
                    }
                    catch
                    {
                        try
                        {
                            return CachedContentManager.GetRepositoryInstance(false);
                        }
                        catch
                        {
                            return null;
                        }
                    }
                });
        }

        /// <summary>
        /// Url optimization service
        /// </summary>
        public IUrlOptimizationService UrlOptimizationService { get; set; }
    }
}
